﻿using System;
using System.Net.NetworkInformation;
using System.Threading;

namespace ThePingMaster
{
    public class PingExecuter
    {
        private const string SuccessLogMessage = "Address {0} successfully reached";
        private const string FailureLogMessage = "Failed to reach address {0}";
        private readonly ManualResetEvent _resetEvent;
        private readonly IConfiguration _configuration;
        private readonly IPingSender _pingSender;
        private readonly ICommandExecuter _commandExecuter;
        private readonly ILogger _logger;

        public PingExecuter(
            ILogger logger,
            IConfiguration configuration,
            IPingSender pingSender,
            ICommandExecuter commandExecuter,
            ManualResetEvent resetEvent)
        {
            if (resetEvent == null)
                throw new ArgumentNullException("resetEvent");
            if (logger == null)
                throw new ArgumentNullException("logger");
            if (configuration == null)
                throw new ArgumentNullException("configuration");
            if (pingSender == null)
                throw new ArgumentNullException("pingSender");
            if (commandExecuter == null)
                throw new ArgumentNullException("commandExecuter");

            _resetEvent = resetEvent;
            _pingSender = pingSender;
            _commandExecuter = commandExecuter;
            _configuration = configuration;
            _logger = logger;
        }

        public void Start()
        {
            do
            {
                foreach (string address in _configuration.Addresses)
                {
                    PingReply reply;
                    int retryCount = 0;
                    bool success = false;
                    do
                    {
                        try
                        {
                            reply = _pingSender.Send(address);
                            success = reply.Status == IPStatus.Success;
                        }
                        catch (Exception pingException)
                        {
                            if (!_configuration.RetryOnNetworkException)
                            {
                                _logger.Error("Exception thrown during ping request", pingException);
                                success = false;
                                retryCount = _configuration.RetryAttemptsBeforeFailure; // bail on exception
                            }
                        }
                        retryCount++;                    
                    }
                    while (!success && retryCount < _configuration.RetryAttemptsBeforeFailure);
                   
                    if (success)
                    {
                        _logger.Info(String.Format(SuccessLogMessage, address));
                        RunSuccessCommand();
                    }
                    else
                    {
                        _logger.Info(String.Format(FailureLogMessage, address));
                        RunFailCommand();
                    }

                    if (_resetEvent.WaitOne(1))
                        break;
                }
            } while (!_resetEvent.WaitOne(_configuration.Interval));
            _logger.Info("Execution thread is ending");
        }

        private void RunFailCommand()
        {
            if (ShouldRunFailCommand)
            {
                _commandExecuter.Execute(_configuration.FailureCommand, _configuration.FailureCommandParams);
            }
        }

        private void RunSuccessCommand()
        {
            if (ShouldRunSuccessCommand)
            {
                _commandExecuter.Execute(_configuration.SuccessCommand, _configuration.SuccessCommandParams);
            }
        }

        private bool ShouldRunFailCommand { get { return !String.IsNullOrEmpty(_configuration.FailureCommand); } }

        private bool ShouldRunSuccessCommand { get { return !String.IsNullOrEmpty(_configuration.SuccessCommand); } }
    }
}
